﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JVMTI_CONN_CONNECT
{
    public class JvmtiEvents : List<JvmtiEvent>
    {
        public LifeCycleViewModels CreateThreadLifeCycleView()
        {
            var groups = this.AsParallel<JvmtiEvent>().GroupBy<JvmtiEvent, string>(jEvent => jEvent.SubjectThread);
            var models = new LifeCycleViewModels();
            var NameDict = Utils.ThreadIdToFriendlyNameMap(this);
            foreach (var group in groups)
            {
                if (group.Count() == 0) continue;
                var model = new LifeCycleViewModel(group.Key, new LifeEvents(group.Select<JvmtiEvent, ThreadLifeEvent>(j => new ThreadLifeEvent(j))));
                model.DisplayName = NameDict[group.Key];
                //model.KeyEvents = model.KeyEvents.OrderBy<JvmtiEvent, double>(o => o.Timestamp);
                models.Add(model);
            }
            return models;
        }

        //see if we need to pull up this with CreateThreadLifeCycleView(). Similar.
        public LifeCycleViewModels CreateResourceLifeCycleView()
        {
            var groups = this.AsParallel<JvmtiEvent>().Where<JvmtiEvent>(o => ((IDictionary<string, object>)o.ExtendedArguments).ContainsKey("Object")).GroupBy<JvmtiEvent, string>(jEvent => jEvent.ExtendedArguments.Object);
            var models = new LifeCycleViewModels();
            foreach (var group in groups)
            {
                if (group.Count() == 0) continue;
                var model = new LifeCycleViewModel(group.Key, new LifeEvents(group.Select<JvmtiEvent, ResourceLifeEvent>(j => new ResourceLifeEvent(j))));
                models.Add(model);
            }
            return models;
        }

        public JvmtiEvents()
        {

        }

        public JvmtiEvents(IEnumerable<JvmtiEvent> events)
        {
            this.AddRange(events);
        }

        //prepare for filtering with observable collection.
    }

    public class JvmtiEvent : INotifyPropertyChanged
    {
        public JvmtiMethodEnum MethodName
        {
            get
            {
                return ExtendedArguments.MethodName;
            }
            set
            {
                ExtendedArguments.MethodName = value;
            }
        }
        public double Timestamp
        {
            get
            {
                return ExtendedArguments.Timestamp;
            }
            set
            {
                ExtendedArguments.Timestamp = value;
            }
        }
        public string SubjectThread
        {
            get
            {
                return ExtendedArguments.SubjectThread;
            }
            set
            {
                ExtendedArguments.SubjectThread = value;
            }
        }
        public dynamic ExtendedArguments { get; set; }
        public JvmtiEvent(string eventStrRep, JvmtiEventCreationOptions creationOptions, string fileInUse)
        {
            rawDatum = eventStrRep;
            ExtendedArguments = new ExpandoObject();
            ExtendedArguments.PropertyChanged = null;
            ExtendedArguments.PropertyChanged += new PropertyChangedEventHandler(notifyPropertyChanged);
            //forces direct cast.
            var Segments = eventStrRep.Split(new[] { ',' });
            // var methodName = (JvmtiMethodEnum)Enum.Parse(typeof(JvmtiMethodEnum), Segments[1], true);
            var methodName = (JvmtiMethodEnum)Enum.Parse(typeof(JvmtiMethodEnum), Segments[1], true);
            //the trick here is to realize expando object is a dict interface.
            var conn = creationOptions;
            for (int i = 0; i < Segments.Length; i++)
            {
                string id;
                var value = conn.ConvertToStrongType(methodName, i, Segments[i], fileInUse, out id);
                ((IDictionary<string, object>)ExtendedArguments).Add(id, value);
                ((IDictionary<string, object>)ExtendedArguments).Add(i.ToString(), Segments[i]);
                //save a copy.
            }
            //ExtendedArguments.StackTrace = StackTrace.EmptyTrace;
            //This object now is ready for direct binding, strong type access, filtering and programatically property retrival.
        }

        void notifyPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, args);
            }
        }
        string rawDatum;
        public override string ToString()
        {
            return rawDatum;
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}
